home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Applications / Publishing / ImagePortfolio / Source / PaletteCell.m < prev    next >
Text File  |  1994-04-01  |  12KB  |  448 lines

  1. // -------------------------------------------------------------------------------------
  2. // PaletteCell.m - image cell
  3. // Martin D. Flynn, NeXT Computer, Inc.
  4. // You may freely copy, distribute and reuse the code in this example.
  5. // NeXT disclaims any warranty of any kind, expressed or implied, as to its
  6. // fitness for any particular use.
  7. // -------------------------------------------------------------------------------------
  8.  
  9. #import "PaletteCell.h"
  10. #import <stdlib.h>
  11. #import <stdio.h>
  12. #import <string.h>
  13. #import <stdarg.h>
  14. #import <math.h>
  15. #import <objc/List.h>
  16. #import <appkit/Cursor.h>
  17. #import <appkit/View.h>
  18. #import <appkit/Text.h>
  19. #import <appkit/NXImage.h>
  20. #import <appkit/NXBitmapImageRep.h>
  21. #import <appkit/nextstd.h>
  22. #import <dpsclient/wraps.h>
  23. #import "PaletteMatrix.h"
  24. #import "ImagePortfolio.h"
  25.  
  26. // -------------------------------------------------------------------------------------
  27. // delegate selectors
  28. #define    becameSELECTED        @selector(cellDidBecomeSelected:)
  29. #define    resignSELECTED        @selector(cellDidResignSelected:)
  30.  
  31. // -------------------------------------------------------------------------------------
  32. #define CELLOFFSET     ((cFlags1.bordered || cFlags1.bezeled)?(cFlags1.bordered?2.0:3.0):0.0)
  33. #define HIGHLIGHTED    ((BOOL)(cFlags1.highlighted || cFlags1.state))
  34.  
  35. // -------------------------------------------------------------------------------------
  36. @implementation PaletteCell
  37.  
  38. // -------------------------------------------------------------------------------------
  39. // support for multiple image types
  40. #define    maxFILETYPES    32
  41. static char    *imageFileExtn[maxFILETYPES + 1];
  42. static List    *imageClassList = (List*)nil;
  43. static int    imageClassCount = 0;
  44.  
  45. /* add image class type */
  46. + addImageClass:classId, ...
  47. {
  48.     int        i;
  49.     char    *cp;
  50.     va_list    args;
  51.   
  52.     /* initialize */
  53.     if (!imageClassList) {
  54.         imageClassList = [[[List alloc] initCount:1] empty];
  55.         memset(imageFileExtn, 0, sizeof(imageFileExtn));
  56.     }
  57.   
  58.     /* add class object */
  59.     [imageClassList addObject:classId];
  60.     imageClassCount = [imageClassList count];
  61.   
  62.     /* add valid extensions */
  63.     for (i = 0; imageFileExtn[i]; i++);    // find end of list
  64.     va_start(args, classId);
  65.     for (;;) if (cp=va_arg(args,char*)) imageFileExtn[i++]=NXCopyStringBuffer(cp); else break;
  66.     va_end(args);
  67.   
  68.     return self;
  69. }
  70.  
  71. /* return true if extension is valid */
  72. BOOL _validExtension(char *fileName)
  73. {
  74.     int    i;
  75.     char    *extn = (char*)fileEXT((char*)fileName);
  76.     for (i = 0; imageFileExtn[i]; i++) if (!strcasecmp(extn, imageFileExtn[i])) return YES;
  77.     return NO;
  78. }
  79.  
  80. // -------------------------------------------------------------------------------------
  81. // new cell methods
  82.  
  83. /* old generic new cell */
  84. + new
  85. {
  86.     return [self newIconCell];
  87. }
  88.  
  89. /* generic new cell */
  90. + newIconCell
  91. {
  92.     self = [self newIconCell:(char*)nil];
  93.     [titleCell setStringValueNoCopy:""];
  94.     return self;
  95. }
  96.  
  97. /* new cell with title */
  98. + newIconCell:(const char *)aString
  99. {
  100.     return [[self allocFromZone:NXDefaultMallocZone()] initIconCell:aString];
  101. }
  102.  
  103. /* generic init */
  104. - init
  105. {
  106.     [self initIconCell:(char*)nil];
  107.     [titleCell setStringValueNoCopy:""];
  108.     return self;
  109. }
  110.  
  111. /* init with title */
  112. - initIconCell:(const char *)aString
  113. {
  114.  
  115.     /* init instance */
  116.     [super initIconCell:(char*)nil];
  117.     [self setStringValueNoCopy:""];
  118.     [self setAlignment:NX_CENTERED];
  119.     titleCell = [[Cell allocFromZone:[self zone]] initTextCell:aString];
  120.     [titleCell setAlignment:NX_CENTERED];
  121.     [self setBezeled:NO];
  122.  
  123.     /* init vars */
  124.     cFlags1.editable = NO;
  125.     cFlags1.selectable = NO;
  126.     cFlags2.noWrap = YES;
  127.     cFlags2._isLeaf = YES;
  128.     delegate = self;
  129.   
  130.     imageId = (id)nil;
  131.     smallImage = (id)nil;
  132.     imagePath = (char*)nil;
  133.     imageMutex = mutex_alloc();
  134.   
  135.     return self;
  136. }
  137.  
  138. /* make an empty copy of this cell */
  139. - copy
  140. {
  141.     NXZone    *zone = [self zone];
  142.     id        tCell = titleCell;
  143.     self = [super copyFromZone:zone];
  144.     titleCell = [[tCell copyFromZone:zone] setFont:[tCell font]];
  145.     imageMutex = mutex_alloc();
  146.     imageId = (id)nil;
  147.     smallImage = (id)nil;
  148.     imagePath = (char*)nil;
  149.     return self;
  150. }
  151.  
  152. /* free this cell */
  153. - free
  154. {
  155.     if (imagePath) free(imagePath);
  156.     if (imageId) [imageId free];
  157.     if (smallImage) [smallImage free];
  158.     [titleCell free];
  159.     mutex_free(imageMutex);
  160.     return [super free];
  161. }
  162.  
  163. /* set matrix owner id */
  164. - setDelegate:anObject
  165. {
  166.     delegate = anObject;
  167.     return self;
  168. }
  169.  
  170. // -------------------------------------------------------------------------------------
  171. // set cell size
  172.  
  173. - setCellSize:(const NXSize*)cellSize
  174. {
  175.     if (smallImage) { [smallImage free]; smallImage = (id)nil; }
  176.     return self;
  177. }
  178.  
  179. - setFont:fontId
  180. {
  181.     return [titleCell setFont:fontId];
  182. }
  183.   
  184. // -------------------------------------------------------------------------------------
  185. // image methods
  186.  
  187. /* load image specified in preset file name */
  188. - loadImageFile
  189. {
  190.     int        i;
  191.     id        tempId = (id)nil;
  192.  
  193.     /* validate load */
  194.     if (!imagePath) return (id)nil;            // no file specified
  195.     if (imageId) return imageId;            // already loaded
  196.  
  197.     /* try all image classes until we find one we like */
  198.     for (i = 0; !tempId && (i < imageClassCount); i++) {
  199.         int j;
  200.         NXSize size;
  201.         id repList = [[imageClassList objectAt:i] newListFromFile:imagePath];
  202.         if (!repList) continue;
  203.         if (![repList count]) { [repList free]; continue; }
  204.         [[repList objectAt:0] getSize:&size];
  205.         if (!size.width || !size.height) { [repList freeObjects]; [repList free]; continue; }
  206.         tempId = [[NXImage alloc] init];
  207.         for (j = 0; j < [repList count]; j++) [tempId useRepresentation:[repList objectAt:j]];
  208.         [repList free];
  209.         mutex_lock(imageMutex);
  210.         imageId = tempId;
  211.         mutex_unlock(imageMutex);
  212.         break;
  213.     }
  214.  
  215.     return tempId;
  216. }
  217.  
  218. /* open image (may be called from non-main thread) */
  219. - setImageFile:(const char *)filePath
  220. {
  221.     char    *iName;
  222.   
  223.     /* free prior storage (actually this should never be necessary!) */
  224.     mutex_lock(imageMutex);
  225.     if (imagePath ) { free(imagePath);   imagePath = (char*)nil; }
  226.     if (imageId   ) { [imageId free];    imageId = (id)nil;      }
  227.     if (smallImage) { [smallImage free]; smallImage = (id)nil;   }
  228.     mutex_unlock(imageMutex);
  229.     
  230.     /* save filename and title */
  231.     imagePath = NXCopyStringBuffer(filePath);
  232.     iName = fileNAME((char*)filePath);
  233.     [titleCell setStringValue:(const char*)iName];    // OK since it can't draw yet
  234.     free(iName);
  235.   
  236.     /* open new image and make sure it was readable */
  237.     return ([self mainThreadPerform:@selector(loadImageFile) wait:YES])?self:(id)nil;
  238.   
  239. }
  240.  
  241. /* return image id */
  242. - image
  243. {
  244.     id    image;
  245.     if (!imagePath) return (id)nil;
  246.     mutex_lock(imageMutex);
  247.     image = imageId;
  248.     mutex_unlock(imageMutex);
  249.     return image;
  250. }
  251.  
  252. /* return image representation */
  253. - imageRepresentation:(int)imageNum
  254. {
  255.     id        repList, image = [self image];
  256.     if (!image) return (id)nil;
  257.     repList = [image representationList];
  258.     return [repList objectAt:(imageNum % [repList count])];
  259. }
  260.  
  261. /* return path to image */
  262. - (const char*)imagePath
  263. {
  264.     return imagePath;
  265. }
  266.  
  267. /* return cell title */
  268. - (const char*)cellTitle
  269. {
  270.     return [titleCell stringValue];
  271. }
  272.  
  273. // -------------------------------------------------------------------------------------
  274.  
  275. - calcCellSize:(NXSize *)theSize inRect:(const NXRect *)aRect
  276. {
  277.     NXRect rect = *aRect;
  278.     NXSize titleSize;
  279.     theSize->width = 1.0;
  280.     theSize->height = 1.0;
  281.     [titleCell calcCellSize:&titleSize inRect:&rect];
  282.     theSize->width += titleSize.width;
  283.     theSize->height = MAX(titleSize.height, theSize->height);
  284.     return self;
  285. }
  286.  
  287. // -------------------------------------------------------------------------------------
  288. // highlight cell
  289.   
  290. /* set selected state */
  291. - setState:(int)flag
  292. {
  293.     if (![self image]) return self;    // ignore if no image in cell
  294.     if ((!cFlags1.state && flag) || (cFlags1.state && !flag)) {
  295.         cFlags1.state = flag? YES : NO;
  296.         if (flag) {
  297.             if (delegate && [delegate respondsTo:@selector(cellBecameSelected:)])
  298.                 [delegate cellBecameSelected:self];
  299.         } else {
  300.             if (delegate && [delegate respondsTo:@selector(cellResignedSelected:)])
  301.                 [delegate cellResignedSelected:self];
  302.         }
  303.     }
  304.     return self;
  305. }
  306.  
  307. /* highlight and select */
  308. - highlight:(const NXRect*)cellFrame inView:controlView lit:(BOOL)flag
  309. {
  310.     if (![self image]) return self;    // ignore if no image in cell
  311.     if ((!cFlags1.highlighted && flag) || (cFlags1.highlighted && !flag)) {
  312.         cFlags1.highlighted = flag? YES : NO;
  313.         [self setState:cFlags1.highlighted];
  314.         [self drawSelf:cellFrame inView:controlView];
  315.     }
  316.     return self;
  317. }
  318.  
  319. /* return highlight state */
  320. - (BOOL)isHighlighted
  321. {
  322.     return HIGHLIGHTED;
  323. }
  324.  
  325. /* return selected state */
  326. - (BOOL)isSelected
  327. {
  328.     return (BOOL)cFlags1.state;
  329. }
  330.   
  331. // -------------------------------------------------------------------------------------
  332. // tool methods
  333.  
  334. /* resize image (only if newSize is smaller than original) (MAIN THREAD ONLY!) */
  335. - _resizeImage:image:(int)num toSize:(NXSize*)toSize
  336. {
  337.     float    aspect;
  338.     NXRect    r = { { 0.0, 0.0 }, { 0.0, 0.0 } };
  339.     id        newImage;
  340.  
  341.     /* return if already within bounds */
  342.     if (!image) return (id)nil;
  343.     [image getSize:&r.size];
  344.     if ((r.size.width <= toSize->width) && (r.size.height <= toSize->height)) return (id)nil;
  345.   
  346.     /* calculate new bounds */
  347.     aspect = r.size.width / r.size.height;
  348.     if (r.size.width  > toSize->width )          r.size.width  = toSize->width ;
  349.     if (r.size.height > toSize->height)          r.size.height = toSize->height;
  350.     if (r.size.width  > r.size.height * aspect) r.size.width  = r.size.height * aspect;
  351.     if (r.size.height > r.size.width  / aspect) r.size.height = r.size.width  / aspect;
  352.  
  353.     /* create a new image */
  354.     newImage = [[NXImage alloc] initSize:&r.size];
  355.     if (!newImage) {
  356.         NXLogError("ImagePortfolio: Unable to init scaled image");
  357.         return (id)nil;
  358.     }
  359.     [newImage setFlipped:[image isFlipped]];
  360.   
  361.     /* draw scaled image */
  362.     PSgsave();
  363.     if ([newImage lockFocus]) {
  364.         [[self imageRepresentation:num] drawIn:&r];
  365.         [newImage unlockFocus];
  366.     } else {
  367.         [newImage free];
  368.         newImage = (id)nil;
  369.     }
  370.     PSgrestore();
  371.  
  372.     /* return resize image */
  373.     return newImage;
  374.   
  375. }
  376.   
  377. // -------------------------------------------------------------------------------------
  378. // smallImage support
  379.  
  380. /* calculate bounding size for small image */
  381. - calcImageSize:(NXSize*)imageSize forCellSize:(NXSize*)cellSize
  382. {
  383.     NXSize    tSize;
  384.     NXRect    cFrame = { {0.0, 0.0}, {0.0, 0.0} };
  385.     cFrame.size = *cellSize;
  386.     NXInsetRect(&cFrame, 1.0, 1.0);
  387.     [titleCell calcCellSize:&tSize inRect:&cFrame];
  388.     cFrame.size.height -= tSize.height + 2.0;
  389.     *imageSize = cFrame.size;
  390.     return self;
  391. }
  392.  
  393. // -------------------------------------------------------------------------------------
  394. // draw methods
  395.  
  396. - drawSelf:(const NXRect *)cellFrame inView:controlView
  397. {
  398.     return [self drawInside:cellFrame inView:controlView];
  399. }
  400.  
  401. - drawInside:(const NXRect *)cellFrame inView:controlView
  402. {
  403.     NXSize    tSize = {0.0, 0.0}, iSize;
  404.     NXRect    rect, cFrame = *cellFrame;
  405.     float    offset = CELLOFFSET;
  406.     BOOL    multi = NO;
  407.     id        image;
  408.  
  409.     /* draw border */
  410.     if (cFlags1.bordered) { PSsetgray(NX_BLACK); NXFrameRectWithWidth(cellFrame, 1.0); } else
  411.     if (cFlags1.bezeled ) { NXDrawGrayBezel(cellFrame, (NXRect*)nil); }
  412.   
  413.     /* inset rectangle and draw highlight */
  414.     NXInsetRect(&cFrame, offset, offset);
  415.     PSsetgray((HIGHLIGHTED)? NX_WHITE: NX_LTGRAY);
  416.     NXRectFill(&cFrame);
  417.     NXInsetRect(&cFrame, 1.0, 1.0);
  418.   
  419.     /* calc text size */
  420.     [titleCell calcCellSize:&tSize inRect:&cFrame];
  421.  
  422.     /* resize & draw image */
  423.     if ([self image]) {
  424.         rect = cFrame;
  425.         rect.size.height -= tSize.height + 2.0;
  426.         rect.origin.y += rect.size.height;
  427.         if ([[imageId representationList] count] > 1) multi = YES;
  428.         if (!smallImage) smallImage = [self _resizeImage:imageId:0 toSize:&rect.size];
  429.         image = (smallImage)? smallImage : imageId;
  430.         [image getSize:&iSize];
  431.         rect.origin.y -= (rect.size.height - iSize.height) / 2.0;
  432.         rect.origin.x += (rect.size.width  - iSize.width ) / 2.0;
  433.         [image composite:NX_SOVER toPoint:&rect.origin];
  434.     }
  435.   
  436.     /* draw image title cell */
  437.     rect = cFrame;
  438.     rect.origin.y += rect.size.height - tSize.height - 1;
  439.     rect.origin.x += floor((rect.size.width - tSize.width) / 2.0);
  440.     rect.size.width  = tSize.width;
  441.     rect.size.height = tSize.height;
  442.     [titleCell drawSelf:&rect inView:controlView];
  443.  
  444.     return self;
  445. }  
  446.  
  447. @end
  448.